From 970cc69c8e0926c2dd1ef4a054652993b3f09d0c Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Tue, 8 Dec 2020 17:26:38 -0600 Subject: [PATCH] Allow overriding of assets --- examples/etc/pgwui.ini | 27 +++++++++++++++ examples/misc/development.ini | 27 +++++++++++++++ src/pgwui_server/pgwui_server.py | 13 +++++-- tests/test_pgwui_server.py | 59 +++++++++++++++++++++++++++----- 4 files changed, 114 insertions(+), 12 deletions(-) diff --git a/examples/etc/pgwui.ini b/examples/etc/pgwui.ini index 1eaa1eb..f382385 100644 --- a/examples/etc/pgwui.ini +++ b/examples/etc/pgwui.ini @@ -152,6 +152,33 @@ pgwui.dry_run = False # pgwui_logout = /logout # pgwui_upload = /upload +# Overriding assets +# +# The visual presentation of a PGWUI component is controlled by its assets. +# Overriding assets allows for extensive customization of presentation. +# +# A Pyramid asset is any file included in a Pyramid application that is +# not a Python source code file. So assets are image files, Mako template +# files used to render pages, CSS files, JavaScript files, etc. +# +# See: +# https://docs.pylonsproject.org/projects/pyramid/en/1.10-branch/narr/assets.html +# +# By default no assets are overridden. Override assets with: +# +# pgwui.override_assets = +# asset = new +# +# 'asset' is the asset to override, a Pyramid asset specification +# +# 'new' is the new value either a Pyramid asset specification +# or a file system path +# +# Example, altering the menu presented by PGWUI_Menu: +# +# pgwui.override_assets = +# pgwui_menu:templates/menu.mak = /tmp/mymenu.mak + # Settings validation # Whether or not to validate the session.secret setting. (optional) diff --git a/examples/misc/development.ini b/examples/misc/development.ini index 9ea841f..80974fa 100644 --- a/examples/misc/development.ini +++ b/examples/misc/development.ini @@ -157,6 +157,33 @@ pgwui.dry_run = False # pgwui_logout = /logout # pgwui_upload = /upload +# Overriding assets +# +# The visual presentation of a PGWUI component is controlled by its assets. +# Overriding assets allows for extensive customization of presentation. +# +# A Pyramid asset is any file included in a Pyramid application that is +# not a Python source code file. So assets are image files, Mako template +# files used to render pages, CSS files, JavaScript files, etc. +# +# See: +# https://docs.pylonsproject.org/projects/pyramid/en/1.10-branch/narr/assets.html +# +# By default no assets are overridden. Override assets with: +# +# pgwui.override_assets = +# asset = new +# +# 'asset' is the asset to override, a Pyramid asset specification +# +# 'new' is the new value either a Pyramid asset specification +# or a file system path +# +# Example, altering the menu presented by PGWUI_Menu: +# +# pgwui.override_assets = +# pgwui_menu:templates/menu.mak = /tmp/mymenu.mak + # Settings validation # Whether or not to validate the session.secret setting. (optional) diff --git a/src/pgwui_server/pgwui_server.py b/src/pgwui_server/pgwui_server.py index 4a4bd54..cb668a6 100644 --- a/src/pgwui_server/pgwui_server.py +++ b/src/pgwui_server/pgwui_server.py @@ -25,10 +25,12 @@ WSGI server. ''' from pyramid.config import Configurator +import pyramid.exceptions import logging import sys from . import exceptions as server_ex +from pgwui_common import assets from pgwui_common import check_settings from pgwui_common import exceptions as common_ex from pgwui_common import routes @@ -53,6 +55,7 @@ MULTI_SETTINGS = set( ['routes', 'home_page', 'menu_page', + 'override_assets', ]) # Default settings @@ -238,7 +241,8 @@ def apply_component_defaults(settings, components): config.include(component) routes.add_routes(config, settings) log.debug('Done autoconfiguring PGWUI components') - return config + errors = assets.override_assets(config, settings) + return (config, errors) def pgwui_server_config(settings): @@ -246,8 +250,11 @@ def pgwui_server_config(settings): ''' components = plugin.find_pgwui_components() exit_on_invalid_settings(settings, components) - config = apply_component_defaults(settings, components) - errors = pgwui_common.urls.add_urls_setting(config, settings) + try: + (config, errors) = apply_component_defaults(settings, components) + except pyramid.exceptions.ConfigurationError as exp: + exit_reporting_errors([common_ex.BadSettingError(exp)]) + errors.extend(pgwui_common.urls.add_urls_setting(config, settings)) if errors: exit_reporting_errors(errors) return config diff --git a/tests/test_pgwui_server.py b/tests/test_pgwui_server.py index 75ffbcb..f64b6b1 100644 --- a/tests/test_pgwui_server.py +++ b/tests/test_pgwui_server.py @@ -23,8 +23,10 @@ import copy import logging import pytest +import pyramid.exceptions import sys +import pgwui_common.assets import pgwui_common.exceptions as common_ex import pgwui_common.check_settings import pgwui_common.plugin @@ -46,6 +48,9 @@ pytestmark = pytest.mark.unittest MockConfigurator = testing.make_magicmock_fixture( pgwui_server, 'Configurator') +mock_override_assets = testing.make_mock_fixture( + pgwui_common.assets, 'override_assets') + mock_find_pgwui_components = testing.make_mock_fixture( pgwui_common.plugin, 'find_pgwui_components') mock_find_pgwui_check_settings = testing.make_mock_fixture( @@ -437,8 +442,12 @@ def test_exit_on_invalid_settings_invalid( errors.append('error1') mock_dictify_settings.side_effect = mymock + mock_exit_reporting_errors.side_effect = lambda *args: sys.exit(1) - pgwui_server.exit_on_invalid_settings({}, []) + with pytest.raises(SystemExit) as excinfo: + pgwui_server.exit_on_invalid_settings({}, []) + + assert excinfo[1].code == 1 mock_dictify_settings.assert_called_once() mock_validate_settings.assert_called_once() @@ -453,7 +462,7 @@ def test_exit_on_invalid_settings_valid( ''' pgwui_server.exit_on_invalid_settings({}, []) - assert True + assert mock_exit_reporting_errors.call_count == 0 mock_exit_on_invalid_settings = testing.make_mock_fixture( @@ -550,17 +559,23 @@ def test_apply_component_defaults(caplog, mock_in_development, MockConfigurator, mock_autoconfigurable_components, - mock_add_routes): + mock_add_routes, + mock_override_assets): '''A configurator is returned, a debug log entry is made for each autoconfigurable component ''' + expected_errors = 'some errors' caplog.set_level(logging.DEBUG) mock_autoconfigurable_components.return_value = \ ['pgwui_mock_component_name'] + mock_override_assets.return_value = expected_errors + + (config, errors) = pgwui_server.apply_component_defaults( + {'pgwui': {}}, []) - result = pgwui_server.apply_component_defaults({'pgwui': {}}, []) - assert str(type(result)) == str(type(MockConfigurator)) + assert str(type(config)) == str(type(MockConfigurator)) + assert errors == expected_errors logs = caplog.record_tuples @@ -585,7 +600,7 @@ mock_add_urls_setting = testing.make_mock_fixture( ('errors', 'ere_calls'), [ ([], 0), (['some error'], 1)]) -def test_pgwui_server_config( +def test_pgwui_server_config_success( pyramid_config, mock_find_pgwui_components, mock_exit_on_invalid_settings, @@ -593,12 +608,18 @@ def test_pgwui_server_config( mock_add_urls_setting, mock_exit_reporting_errors, errors, ere_calls): - '''Makes expected calls, returns a configuration + '''When apply_component_defaults() returns -- makes expected calls, + returns a configuration ''' - mock_apply_component_defaults.return_value = pyramid_config + mock_apply_component_defaults.return_value = (pyramid_config, []) mock_add_urls_setting.return_value = errors + mock_exit_reporting_errors.side_effect = lambda *args: sys.exit(1) - result = pgwui_server.pgwui_server_config({}) + if ere_calls: + with pytest.raises(SystemExit): + result = pgwui_server.pgwui_server_config({}) + else: + result = pgwui_server.pgwui_server_config({}) mock_find_pgwui_components.assert_called_once() mock_exit_on_invalid_settings.assert_called_once() @@ -609,6 +630,26 @@ def test_pgwui_server_config( assert result == pyramid_config +def test_pgwui_server_config_bad_config( + pyramid_config, + mock_find_pgwui_components, + mock_exit_on_invalid_settings, + mock_apply_component_defaults, + mock_add_urls_setting, + mock_exit_reporting_errors): + '''When apply_component_defaults() raises a configuration exception + exit_reporting_errors() is called + ''' + mock_apply_component_defaults.side_effect = \ + pyramid.exceptions.ConfigurationError + mock_exit_reporting_errors.side_effect = lambda *args: sys.exit(1) + + with pytest.raises(SystemExit): + pgwui_server.pgwui_server_config({}) + + assert mock_exit_reporting_errors.call_count == 1 + + mock_pgwui_server_config = testing.make_mock_fixture( pgwui_server, 'pgwui_server_config') -- 2.34.1